import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import os
import json
import threading
import sys
import logging
import traceback
import multiprocessing

# 设置日志
logging.basicConfig(
    filename='gui.log',
    level=logging.DEBUG,
    format='%(asctime)s - %(levelname)s - %(message)s',
    encoding='utf-8'
)

# 记录 Python 和系统信息
logging.info(f"Python 版本: {sys.version}")
logging.info(f"操作系统: {sys.platform}, {os.name}")

# 设置 multiprocessing 启动方式
multiprocessing.set_start_method("spawn", force=True)
logging.info("已设置 multiprocessing 启动方式为 spawn")

# 导入主逻辑
try:
    from wjx import run_main
    logging.info("成功导入 wjx 模块")
except ImportError as e:
    logging.error(f"无法导入必要的模块: {e}\n{traceback.format_exc()}")
    messagebox.showerror("导入错误", f"无法导入必要的模块: {e}\n请确保 gui.py 和 wjx.py 在同一目录下。")
    sys.exit(1)

# --- 配置变量 (作为默认值) ---
DRIVER_PATH = r"G:\msedgedriver.exe"
BROWSER_PATH = r"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe"
PROCESSES_COUNT = 1
RUNS_PER_PROCESS = 1
USE_PROXY = False
API_URL = 'https://service.ipzan.com/core-extract?num=1&no=20230525165982903521&minute=1&format=json&protocol=1&pool=quality&secret=uvg8pco497ldfag'

SETTINGS_FILE = "gui_settings.json"
API_URL_FIXED = 'https://service.ipzan.com/core-extract?num=1&no=20230525165982903521&minute=1&format=json&protocol=1&pool=quality&secret=uvg8pco497ldfag'

class SurveyApp(tk.Tk):
    def __init__(self):
        logging.info("开始初始化 SurveyApp...")
        try:
            super().__init__()
            logging.info("tk.Tk 初始化成功")
            self.title("问卷自动提交工具 (带记忆功能)")
            self.geometry("550x420")
            logging.info("设置窗口标题和大小成功")
        except Exception as e:
            logging.error(f"tk.Tk 初始化失败: {e}\n{traceback.format_exc()}")
            raise

        # 变量定义
        try:
            self.json_file_var = tk.StringVar()
            # [优化] 修正了变量与其对应默认值的匹配关系
            self.browser_path_var = tk.StringVar(value=BROWSER_PATH)
            self.driver_path_var = tk.StringVar(value=DRIVER_PATH)
            self.process_count_var = tk.IntVar(value=PROCESSES_COUNT)
            self.runs_per_process_var = tk.IntVar(value=RUNS_PER_PROCESS)
            self.use_proxy_var = tk.BooleanVar(value=USE_PROXY)
            logging.info("变量初始化成功")
        except Exception as e:
            logging.error(f"变量初始化失败: {e}\n{traceback.format_exc()}")
            raise

        # 加载设置
        self.load_settings()

        # 创建 UI
        try:
            self.create_widgets()
            logging.info("UI 创建成功")
        except Exception as e:
            logging.error(f"创建 UI 失败: {e}\n{traceback.format_exc()}")
            raise

        # 绑定关闭事件
        try:
            self.protocol("WM_DELETE_WINDOW", self.on_closing)
            logging.info("窗口关闭事件绑定成功")
        except Exception as e:
            logging.error(f"窗口关闭事件绑定失败: {e}\n{traceback.format_exc()}")
            raise

        logging.info("SurveyApp 初始化完成")

    def load_settings(self):
        logging.info("加载设置...")
        try:
            if os.path.exists(SETTINGS_FILE):
                with open(SETTINGS_FILE, 'r', encoding='utf-8') as f:
                    settings = json.load(f)
                self.json_file_var.set(settings.get('json_file', ''))
                # [优化] 修正了从配置文件加载时，备用默认值的对应关系
                self.browser_path_var.set(settings.get('browser_path', BROWSER_PATH))
                self.driver_path_var.set(settings.get('driver_path', DRIVER_PATH))
                self.process_count_var.set(settings.get('processes', PROCESSES_COUNT))
                self.runs_per_process_var.set(settings.get('runs', RUNS_PER_PROCESS))
                self.use_proxy_var.set(settings.get('use_proxy', USE_PROXY))
                logging.info(f"成功加载设置: {SETTINGS_FILE}")
            else:
                logging.info("未找到设置文件，使用默认值")
        except (json.JSONDecodeError, IOError) as e:
            logging.error(f"加载设置失败: {e}\n{traceback.format_exc()}")
            messagebox.showwarning("加载失败", f"加载配置文件 '{SETTINGS_FILE}' 失败: {e}。\n将使用默认值。")

    def save_settings(self):
        logging.info("保存设置...")
        settings = {
            'json_file': self.json_file_var.get(),
            'browser_path': self.browser_path_var.get(),
            'driver_path': self.driver_path_var.get(),
            'processes': self.process_count_var.get(),
            'runs': self.runs_per_process_var.get(),
            'use_proxy': self.use_proxy_var.get()
        }
        try:
            with open(SETTINGS_FILE, 'w', encoding='utf-8') as f:
                json.dump(settings, f, indent=4)
            logging.info("设置保存成功")
        except IOError as e:
            logging.error(f"保存设置失败: {e}\n{traceback.format_exc()}")
            messagebox.showerror("保存失败", f"保存设置时出错: {e}")

    def on_closing(self):
        logging.info("窗口关闭，保存设置...")
        self.save_settings()
        self.destroy()

    def create_widgets(self):
        main_frame = ttk.Frame(self, padding="15")
        main_frame.pack(fill=tk.BOTH, expand=True)

        ttk.Label(main_frame, text="问卷配置文件:").grid(row=0, column=0, sticky="w", pady=5)
        json_entry = ttk.Entry(main_frame, textvariable=self.json_file_var, width=50)
        json_entry.grid(row=1, column=0, sticky="ew", padx=(0, 5))
        ttk.Button(main_frame, text="选择...", command=self.select_json_file).grid(row=1, column=1, sticky="w")

        ttk.Label(main_frame, text="Edge 浏览器路径 (msedge.exe):").grid(row=2, column=0, sticky="w", pady=(10, 5))
        browser_entry = ttk.Entry(main_frame, textvariable=self.browser_path_var, width=50)
        browser_entry.grid(row=3, column=0, sticky="ew", padx=(0, 5))
        ttk.Button(main_frame, text="选择...", command=self.select_browser_path).grid(row=3, column=1, sticky="w")

        ttk.Label(main_frame, text="Edge 驱动路径 (msedgedriver.exe):").grid(row=4, column=0, sticky="w", pady=(10, 5))
        driver_entry = ttk.Entry(main_frame, textvariable=self.driver_path_var, width=50)
        driver_entry.grid(row=5, column=0, sticky="ew", padx=(0, 5))
        ttk.Button(main_frame, text="选择...", command=self.select_driver_path).grid(row=5, column=1, sticky="w")

        count_frame = ttk.Frame(main_frame)
        count_frame.grid(row=6, column=0, columnspan=2, sticky="ew", pady=15)

        ttk.Label(count_frame, text="浏览器窗口数:").pack(side=tk.LEFT, padx=(0, 5))
        ttk.Spinbox(count_frame, from_=1, to=os.cpu_count() or 16, textvariable=self.process_count_var, width=5).pack(
            side=tk.LEFT, padx=5)

        ttk.Label(count_frame, text="每个窗口运行次数:").pack(side=tk.LEFT, padx=(15, 5))
        ttk.Spinbox(count_frame, from_=1, to=1000, textvariable=self.runs_per_process_var, width=5).pack(side=tk.LEFT, padx=5)

        proxy_check = ttk.Checkbutton(main_frame, text="使用代理IP", variable=self.use_proxy_var)
        proxy_check.grid(row=7, column=0, columnspan=2, sticky="w", pady=10)

        self.run_button = ttk.Button(main_frame, text="开始执行", command=self.start_task)
        self.run_button.grid(row=8, column=0, columnspan=2, pady=20)

        main_frame.columnconfigure(0, weight=1)

    def select_json_file(self):
        logging.info("打开 JSON 文件选择对话框...")
        filepath = filedialog.askopenfilename(
            title="选择问卷 JSON 文件",
            filetypes=[("JSON files", "*.json")]
        )
        if filepath:
            self.json_file_var.set(filepath)
            logging.info(f"已选择 JSON 文件: {filepath}")
        else:
            logging.info("未选择 JSON 文件")

    def select_path(self, var_to_set, title, filetypes):
        # 这是一个通用的文件选择函数，提高代码复用性
        filepath = filedialog.askopenfilename(title=title, filetypes=filetypes)
        if filepath:
            var_to_set.set(filepath)
            logging.info(f"已为 '{title}' 选择路径: {filepath}")
        else:
            logging.info(f"未给 '{title}' 选择任何路径")

    def select_browser_path(self):
        self.select_path(self.browser_path_var, "选择 Edge 浏览器 (msedge.exe)", [("可执行文件", "*.exe")])

    def select_driver_path(self):
        self.select_path(self.driver_path_var, "选择 Edge 驱动 (msedgedriver.exe)", [("可执行文件", "*.exe")])

    def start_task(self):
        logging.info("点击开始执行，保存设置...")
        self.save_settings()

        app_config = {
            'SURVEY_CONFIG_FILE': self.json_file_var.get(),
            'BROWSER_PATH': self.browser_path_var.get(),
            'DRIVER_PATH': self.driver_path_var.get(),
            'PROCESSES_COUNT': self.process_count_var.get(),
            'RUNS_PER_PROCESS': self.runs_per_process_var.get(),
            'USE_PROXY': self.use_proxy_var.get(),
            'API_URL': API_URL
        }

        # 增强路径验证
        if not app_config['SURVEY_CONFIG_FILE']:
            messagebox.showerror("错误", "请选择一个问卷配置文件！")
            logging.error("未选择问卷配置文件")
            return
        if not os.path.exists(app_config['SURVEY_CONFIG_FILE']):
            messagebox.showerror("错误", f"问卷配置文件不存在！\n路径: {app_config['SURVEY_CONFIG_FILE']}")
            logging.error(f"问卷配置文件不存在: {app_config['SURVEY_CONFIG_FILE']}")
            return
        if not os.path.exists(app_config['BROWSER_PATH']):
            messagebox.showerror("错误", f"浏览器路径无效或文件不存在！\n路径: {app_config['BROWSER_PATH']}")
            logging.error(f"浏览器路径无效: {app_config['BROWSER_PATH']}")
            return
        if not os.path.exists(app_config['DRIVER_PATH']):
            messagebox.showerror("错误", f"驱动路径无效或文件不存在！\n路径: {app_config['DRIVER_PATH']}")
            logging.error(f"驱动路径无效: {app_config['DRIVER_PATH']}")
            return

        self.run_button.config(state="disabled", text="正在运行...")
        task_thread = threading.Thread(target=self.run_task_in_thread, args=(app_config,))
        task_thread.daemon = True
        task_thread.start()
        logging.info("已启动任务线程")

    def run_task_in_thread(self, app_config):
        try:
            logging.info("任务线程开始执行 run_main...")
            run_main(app_config)
            self.after(0, lambda: messagebox.showinfo("完成", "所有问卷任务已执行完毕！"))
            logging.info("任务执行完成")
        except Exception as e:
            logging.error(f"任务执行失败: {e}\n{traceback.format_exc()}")
            self.after(0, lambda: messagebox.showerror("运行时错误", f"执行过程中出现严重错误: \n{e}"))
        finally:
            self.after(0, lambda: self.run_button.config(state="normal", text="开始执行"))
            logging.info("任务线程结束")


if __name__ == '__main__':
    # =========================================================================
    #  关键改动：在程序入口处立即调用 freeze_support()
    #  这是解决打包后多进程重复启动界面的标准方法。
    multiprocessing.freeze_support()
    # =========================================================================

    logging.info("程序启动")
    logging.info(f"PyInstaller 环境: {getattr(sys, 'frozen', False)}")
    logging.info(f"当前进程: {multiprocessing.current_process().name}")

    if multiprocessing.current_process().name == 'MainProcess':
        try:
            app = SurveyApp()
            app.mainloop()
        except Exception as e:
            logging.error(f"GUI 启动失败: {e}\n{traceback.format_exc()}")
            messagebox.showerror("致命错误", f"应用启动失败: {e}")
            raise
    else:
        logging.info("在子进程中，跳过 GUI 启动")